//-------------------------------------------------------------------
//	Copyright (c) 2010-2012  Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#ifndef _UX_EXTENDED_STRING_H
#define _UX_EXTENDED_STRING_H

#include "../Defines/Defines.h"

namespace Engine
{
namespace UXTypes
{

	//
	// Type switch
	//

	template <uint S>	struct _TExtStringTypeSwitch					{};
	template <>			struct _TExtStringTypeSwitch< sizeof(uint8) >	{ typedef uint8   type; };
	template <>			struct _TExtStringTypeSwitch< sizeof(uint16) >	{ typedef uint16  type; };
	template <>			struct _TExtStringTypeSwitch< sizeof(uint32) >	{ typedef uint32  type; };
	template <>			struct _TExtStringTypeSwitch< sizeof(uint64) >	{ typedef uint64  type; };



	//
	// Extended String
	//

	template <typename T, typename S = TFastCopyStrategy<T>, typename A = TAllocator<T> >
	struct TExtString
	{
	public:
		enum EChunkType
		{
			UNKNOWN	= 0,
			COLOR,
			SCALE,
			SPACING,
			// for reader state only
			END		= 0xEE,
			STRING	= 0xFF,
		};

		struct TChunk
		{
			EChunkType	eType;
			union {
				float	fValue;		// scale, spacing
				int32	iValue;		// color
			};
			///
			TChunk(): eType(UNKNOWN), iValue(0) {}
			TChunk(EChunkType t, int i): eType(t), iValue(i) {}
			TChunk(EChunkType t, float f): eType(t), fValue(f) {}
		};
		

		struct TReader;

		typedef TExtString<T,S,A>										Tthis;
		typedef typename _TExtStringTypeSwitch< sizeof(T)*2 >::type		chunk_code_t;


	private:
		struct _ECode {
			enum type
			{
				END		= 0,
				LENGTH	= 0x0001,	// uint + T[]
				EMPTY	= 0x0002,	// uint + 0
				SCALE	= 0x00E1,	// float
				SPACING	= 0x00E2,	// float
				COLOR	= 0x00EC,	// uint == uint8[4]
			};
		};

		// values
		array<T,S,A>	_aString;


		// methods
		static EChunkType				_s_CodeToChunk(typename _ECode::type code);
		static typename _ECode::type	_s_ChunkToCode(EChunkType chunk);


	public:
		TExtString() {}
		TExtString(const TUniString<T> &s);
		~TExtString() {}


		Tthis &	Append(const TUniString<T> &s);
		Tthis &	Append(const TExtString<T> &s);
		Tthis &	Append(const TChunk &c);

		// chunks
		Tthis &	AddColor(uint32 uRGBA);
		Tthis &	AddScale(float fScale);
		Tthis &	AddSpacing(float fSpacing);


		void Copy(const TUniString<T> &s);
		void Copy(const TExtString<T> &s);

		void Clear();
		void ClearMem();

		void Parse(const TUniString<T> &s);

		TReader	GetReader() const;
	};



	//
	// Extended String Reader
	//

	template <typename T, typename S = TFastCopyStrategy<T>, typename A = TAllocator<T> >
	struct TExtString<T,S,A>::TReader
	{
	private:
		// types
		typedef TExtString<T,S,A>				estr_t;
		typedef typename estr_t::chunk_code_t	chunk_code_t;
		typedef typename estr_t::EChunkType		EChunkType;
		typedef TReader							Tthis;

		union UCharsToCode
		{
			chunk_code_t	uCode;
			T				aChars[2];
			///
			UCharsToCode(chunk_code_t c): uCode(c) {}
			UCharsToCode(T c0, T c1) { aChars[0] = c0; aChars[1] = c1; }
		};

		template <typename B>
		union UCharsToType
		{
			enum {
				CHARS_COUNT	= sizeof(B) / sizeof(T)
			};

			B		tType;
			T		aChars[ CHARS_COUNT ];
			///
			UCharsToType(const T *p) { memcpy( aChars, p, sizeof(aChars) ); }
		};


		// values
		const array<T,S,A> &	_aString;
		usize					_uPos;
		EChunkType				_eState;

	public:
		TReader(const TExtString<T,S,A> &s);
		
		EChunkType			NextChunk();
		EChunkType			GetState()		const		{ return _eState; }

		float				GetChunkFloat();
		int32				GetChunkInt();
		const TUniString<T>	GetString();

	};

	


	typedef TExtString< char,  TFastCopyStrategy<char>,  TAllocator<char>  >		ext_string;
	typedef	TExtString< wchar, TFastCopyStrategy<wchar>, TAllocator<wchar> >		ext_wstring;



	


	
	template <typename T, typename S, typename A>
	inline TExtString<T,S,A>::TExtString(const TUniString<T> &s)
	{
		Parse( s );
	}
		
	
	template <typename T, typename S, typename A>
	inline typename TExtString<T,S,A>::EChunkType  TExtString<T,S,A>::_s_CodeToChunk(typename _ECode::type code)
	{
		switch ( code )
		{
			case _ECode::END		: return END;
			case _ECode::LENGTH		: return STRING;
			case _ECode::SCALE		: return SCALE;
			case _ECode::SPACING	: return SPACING;
			case _ECode::COLOR		: return COLOR;
		};

		return UNKNOWN;
	}

	
	template <typename T, typename S, typename A>
	inline typename TExtString<T,S,A>::_ECode::type  TExtString<T,S,A>::_s_ChunkToCode(EChunkType chunk)
	{
		switch ( chunk )
		{
			case END		: return _ECode::END;
			case STRING		: return _ECode::LENGTH;
			case SCALE		: return _ECode::SCALE;
			case SPACING	: return _ECode::SPACING;
			case COLOR		: return _ECode::COLOR;
		};

		return _ECode::EMPTY;
	}

	
	template <typename T, typename S, typename A>
	inline TExtString<T,S,A> &  TExtString<T,S,A>::Append(const TUniString<T> &s)
	{
		const chunk_code_t	u_chunk		= _ECode::LENGTH;
		const usize			u_length	= s.Length()+1;

		_aString.Append( uni_array<T>( &u_chunk ) );
		_aString.Append( uni_array<T>( &u_length ) );
		_aString.Append( s );
		return *this;
	}

	
	template <typename T, typename S, typename A>
	inline TExtString<T,S,A> &  TExtString<T,S,A>::Append(const TExtString<T> &s)
	{
		_aString.Append( uni_array<T>( s ) );
		return *this;
	}

	
	template <typename T, typename S, typename A>
	inline TExtString<T,S,A> &  TExtString<T,S,A>::Append(const TChunk &c)
	{
		switch ( c.eType )
		{
			case SCALE		: AddScale( c.fValue );		break;
			case SPACING	: AddSpacing( c.fValue );	break;
			case COLOR		: AddColor( c.iValue );		break;
		};
		return *this;
	}

	
	template <typename T, typename S, typename A>
	inline void TExtString<T,S,A>::Copy(const TUniString<T> &s)
	{
		_aString.ClearMem();
		Append( s );
	}

	
	template <typename T, typename S, typename A>
	inline void TExtString<T,S,A>::Copy(const TExtString<T> &s)
	{
		_aString.Copy( s );
	}

	
	template <typename T, typename S, typename A>
	inline void TExtString<T,S,A>::Clear()
	{
		_aString.Clear();
	}

	
	template <typename T, typename S, typename A>
	inline void TExtString<T,S,A>::ClearMem()
	{
		_aString.ClearMem();
	}


	template <typename T, typename S, typename A>
	inline TExtString<T,S,A> &  TExtString<T,S,A>::AddColor(uint32 uRGBA)
	{
		const chunk_code_t	u_code = _ECode::COLOR;

		_aString.Append( uni_array<T>( &u_code ) );
		_aString.Append( uni_array<T>( &uRGBA ) );

		return *this;
	}

	
	template <typename T, typename S, typename A>
	inline TExtString<T,S,A> &  TExtString<T,S,A>::AddScale(float fScale)
	{
		const chunk_code_t	u_code = _ECode::SCALE;

		_aString.Append( uni_array<T>( &u_code ) );
		_aString.Append( uni_array<T>( &fScale ) );

		return *this;
	}

	
	template <typename T, typename S, typename A>
	inline TExtString<T,S,A> &  TExtString<T,S,A>::AddSpacing(float fSpacing)
	{
		const chunk_code_t	u_code = _ECode::SPACING;

		_aString.Append( uni_array<T>( &u_code ) );
		_aString.Append( uni_array<T>( &fSpacing ) );
	
		return *this;
	}


	template <typename T, typename S, typename A>
	inline typename TExtString<T,S,A>::TReader  TExtString<T,S,A>::GetReader() const
	{
		return TReader( *this );
	}

		
	template <typename T, typename S, typename A>
	inline void  TExtString<T,S,A>::Parse(const TUniString<T> &s)
	{
		static const TStaticString<T,6>	s_sColor( "color" );
		static const TStaticString<T,6>	s_sScale( "scale" );
		static const TStaticString<T,8>	s_sSpacing( "spacing" );

		ClearMem();
		
		EChunkType	e_mode	= STRING;
		usize		u_last	= 0;
		usize		i		= 0;

		for (; i < s.Length();)
		{
			// find key
			if ( s[i] != '#' || s[i+1] != '[' ) {
				++i;
				continue;
			}

			if ( u_last != i )
				Append( TString<T,S,A>( (T*)s.ptr() + u_last, i - u_last ) );

			i += 2;
			u_last = i;

			if ( i >= s.Length() ) break;

			while ( s[i] != ':' && s[i] != ']' && s[i] != 0 ) { ++i; }

			if ( s[i] != ':' ) {
				WARNING( "wrong char" );
				continue;
			}

			TUniString<T>	s_chunk( (T*)s.ptr() + u_last, i - u_last );
			u_last = ++i;

			while ( s[i] != ']' && s[i] != 0 ) { ++i; }

			TUniString<T>	s_value( (T*)s.ptr() + u_last, i - u_last );
			u_last = ++i;

			if ( s_chunk.Compare( s_sColor, 0, s_chunk.Length() ) ) {
				AddColor( (uint32)String::FromStrR16ToInt( s_value ) );
			}
			else
			if ( s_chunk.Compare( s_sScale, 0, s_chunk.Length() ) ) {
				AddScale( (float)String::FromStrToFloat( s_value ) );
			}
			else
			if ( s_chunk.Compare( s_sSpacing, 0, s_chunk.Length() ) ) {
				AddSpacing( (float)String::FromStrToFloat( s_value ) );
			}
			else
				WARNING( "unknonw chunk type" );
		}

		if ( u_last != i )
			Append( TString<T,S,A>( (T*)s.ptr() + u_last, i - u_last ) );
	}
	
	



	template <typename T, typename S, typename A>
	inline TExtString<T,S,A>::TReader::TReader(const TExtString<T,S,A> &s):
		_aString(s._aString), _uPos(0), _eState(estr_t::UNKNOWN)
	{}

	
	template <typename T, typename S, typename A>
	inline typename TExtString<T,S,A>::EChunkType  TExtString<T,S,A>::TReader::NextChunk()
	{
		if ( _eState == estr_t::END ||
			 _uPos+2 >= _aString.Count() )
		{
			return ( _eState = estr_t::END );
		}

		const UCharsToCode	s_code( _aString[_uPos], _aString[_uPos+1] );

		_uPos += 2;
		return _eState = estr_t::_s_CodeToChunk( (estr_t::_ECode::type)s_code.uCode );
	}

	
	template <typename T, typename S, typename A>
	inline float  TExtString<T,S,A>::TReader::GetChunkFloat()
	{
		ASSERT( _eState == estr_t::SCALE || _eState == estr_t::SPACING );

		const UCharsToType<float>	s_value( _aString.ptr() + _uPos );

		_uPos  += s_value.CHARS_COUNT;
		_eState = estr_t::UNKNOWN;

		return s_value.tType;
	}


	
	template <typename T, typename S, typename A>
	inline int32  TExtString<T,S,A>::TReader::GetChunkInt()
	{
		ASSERT( _eState == estr_t::COLOR );

		const UCharsToType<int32>	s_value( _aString.ptr() + _uPos );

		_uPos  += s_value.CHARS_COUNT;
		_eState = estr_t::UNKNOWN;

		return s_value.tType;
	}


	
	template <typename T, typename S, typename A>
	inline const TUniString<T>  TExtString<T,S,A>::TReader::GetString()
	{
		ASSERT( _eState == estr_t::STRING );

		const UCharsToType<usize>		s_length( _aString.ptr() + _uPos );

		_uPos += s_length.CHARS_COUNT;

		const T *	p_begin = _aString.ptr() + _uPos;
		const T *	p_end	= _aString.ptr() + _uPos + s_length.tType;

		_uPos  += s_length.tType;
		_eState = estr_t::UNKNOWN;

		return TUniString<T>( (void *)p_begin, (void *)p_end );
	}

//-------------------------------------------------------------------

}	// UXTypes
}	// Engine

#endif	// _UX_EXTENDED_STRING_H